/*
 * @Author: Fan Dong
 * @Date: 2021-07-09 11:29:19
 * @LastEditors: Fan Dong
 * @LastEditTime: 2021-07-09 17:17:43
 * @Description: 文件描述
 * @FilePath: \learn-typescript\ts\10.装饰器.ts
 */

/*
装饰器： 装饰器是一种特殊类型的声明，他能被附加到类声明，方法，属性或参数上，可以修改类的行为。

通俗的讲装饰器就是一个方法，可以注入到类、方法、属性参数上来扩展类、方法、参数的功能。

常见的装饰器有：类装饰器、属性装饰器、方法装饰器、参数装饰器

装饰器的写法：普通装饰器（无法传参）、装饰器工厂（可传参）

装饰器是过去几年中js最大的成就之一，已是es7的标准特性之一
 
*/
// 1、类装饰器： 类装饰器在类声明之前被声明（紧靠着类申明）。类装饰器应用于类构造函数，可以用来监视，修改或替换类定义。传入一个参数。

/*
//普通装饰器（无法传参）
function logClass(params:any) {
  console.log(params)
  // params就是当前类

  params.prototype.apiUrl='动态扩展的属性';
  
  params.prototype.run = function () {
    console.log("我是run方法")
  }
}


@logClass
class HttpClient {
  constructor() {}
  getData() {}
}


let http:any = new HttpClient();
console.log(http.apiUrl)
http.run()
*/

/*
// 装饰器工厂（可传参）
function logClass(params: string) {
  return function (target: any) {
    console.log(params);
    console.log(target);
    target.prototype.apiUrl = params;
  };
}

@logClass("http://www.baidu.com")
class HttpClient {
  constructor() {}
  getData() {}
}

let http:any = new HttpClient();
console.log(http.apiUrl)
*/

/*
下面是一个重载构造函数的例子。
类装饰器表达式会在运行时当做函数被调用，类的构造函数作为其唯一的参数。
如果类装饰器返回一个值，他会使用提供的构造函数来替换类的声明
*/

/*
function logClass(target: any) {
  console.log(target);

  return class extends target {
    apiUrl: any = "我是修改后的数据";
    
    getData(){
      this.apiUrl+='--'
      console.log(this.apiUrl)
    }
  };
}

@logClass
class HttpClient {
  apiUrl: string | undefined;
  constructor() {
    this.apiUrl = "我是构造函数里的api";
  }
  getData() {
    console.log(this.apiUrl);
  }
}

let http = new HttpClient();
http.getData();
*/

/*
2、属性装饰器
    属性装饰器表达式会在运行时当做函数被调用，传入下列2个参数
      1.对于静态成员来说是类的构造函数，对于实例成员是类的原型对象
      2.成员的名称

*/
/*
// 类装饰器
function logClass(params: string) {
  return function (target: any) {
    // console.log(target);
    // console.log(params);
  };
}

// 属性装饰器

function logProperty(params:any) {
  return function (target:any,attr:any) {
    console.log(target)
    console.log(attr)
    target[attr] = params;
  }
}

@logClass('xxxxx')
class HttpClient {

  @logProperty('http://www.baidu.com')
  public url: any | undefined;
  constructor() {}
  getData() {
    console.log(this.url)
  }
}

let http1 = new HttpClient();
http1.getData();
*/

/*
3、方法装饰器

      它会被应用到方法的属性描述上，可以用来监视，修改或替换方法定义。

      方法装饰器会在运行时加入下列3个参数：
        1、对于静态成员来说是类的构造函数，对于实例成员是类的原型对象。
        2、成员的名称。
        3、成员的属性描述符。

*/
/*
// 方法装饰器一  修改实例和属性
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc);

    target.apiUrl = "xzxzz";
    target.run = function () {
      console.log("run");
    };
  };
}

class HttpClient {
  url: any | undefined;
  constructor() {}
  @get("http://www.baidu.com")
  getData() {
    console.log(this.url);
  }
}

let h:any= new HttpClient();
h.getData();
h.run();
console.log(h.apiUrl);
*/
/*
// 方法装饰器二 修改当前的方法
function get(params: any) {
  return function (target: any, methodName: any, desc: any) {
    console.log(target);
    console.log(methodName);
    console.log(desc.value);

    // 修改装饰器的方法  把装饰器方法里面传入所有参数改为string类型
    // 1.保存当前的方法
    let oMethod = desc.value;

    desc.value = function (...args: any[]) {
      args = args.map((value) => {
        return String(value);
      });
      console.log(args);
      oMethod.apply(this,args)
    };
  };
}

class HttpClient {
  url: any | undefined;
  constructor() {}
  @get("http://www.baidu.com")
  getData(...args:any[]) {
    console.log(args)
    console.log('我是getData里面的方法');
  }
}

let h2 = new HttpClient();
h2.getData(123,'xxxx');

*/

/*
4、方法参数装饰器：
  参数装饰器表达式会在运行时当做函数被调用，可以使用参数装饰器为类的原型增加一些元素数据，传入下列3个参数：
    1、对于静态成员来说是类的构造函数，对于实例来说是类的原型对象。
    2、方法的名称。
    3、参数在函数参数列表中的索引。

*/
/*
function logParams(params: any) {
  return function (target: any, methodName: any, paramsIndex: any) {
    console.log(params)
    console.log(target);
    console.log(methodName);
    console.log(paramsIndex);

    target.apiUrl = params;
  };
}
class HttpClient {
  url: any | undefined;
  constructor() {}
  getData(@logParams('xxxx') uuid:any) {
    console.log(uuid);
  }
}

let h:any = new HttpClient();
h.getData(123456)
console.log(h.apiUrl)

*/

// 装饰器的执行顺序  
// 属性->方法 -> 方法参数器-> 类装饰器
// 如果有多个同样的装饰器，它会先执行后面的

function logClass1(params:string) {
  return function (target:any) {
    console.log('类装饰器1')
  }
}

function logClass2(params:string) {
  return function (target:any) {
    console.log('类装饰器2')
  }
}
function logAttribute(params?:string) {
  return function (target:any, attrName: any) {
    console.log('属性装饰器')
  }
}
function logMethod(params?:string) {
  return function (target:any, attrName: any) {
    console.log('方法装饰器')
  }
}

function logParams1(params?:string) {
  return function (target:any, attrName: any) {
    console.log('方法参数装饰器1')
  }
}
function logParams2(params?:string) {
  return function (target:any, attrName: any) {
    console.log('方法参数装饰器2')
  }
}
@logClass1('http://www.baidu.com')
@logClass2('xxxx')
class HttpClient {
  @logAttribute()
  apiUrl: any | undefined;
  
  constructor() {}

  @logMethod()
  getData() {
    return true;
  }


  setData(@logParams1() atta1:any,@logParams2() atta2:any){

  }
}

let h = new HttpClient();